
package cfar.ff.data;

import cfar.ff.fun.F1;
import cfar.ff.interfaces.Empty;
import cfar.ff.interfaces.Functor;
import cfar.ff.interfaces.Sequence;
import java.util.Collections;
import java.util.Iterator;

/**
 *
 * @author badi
 */
public class Stream<A> implements Sequence<Stream, A> {

    final fj.data.Stream<A> stream;

    public Stream(){
        stream = fj.data.Stream.nil();
    }

    private Stream(fj.data.Stream<A> stream){
        this.stream = stream;
    }

    public Stream(Iterable<A> seq){
        stream = fj.data.Stream.iterableStream(seq);
    }

    public static <A> Stream<A> stream(final A... as){
        return new Stream(fj.data.Stream.<A>stream(as));
    }

    public static <A> Stream<A> nil(){
        return new Stream(fj.data.Stream.<A>nil());
    }

    public Sequence<Stream, A> cycle(){
        return new Stream(stream.cycle(stream));
    }

    public Sequence<Stream, A> cons(A a) {
        return new Stream(stream.cons(a));
    }

    public Sequence<Stream, A> snoc(A a) {
        return new Stream(stream.snoc(a));
    }

    public A head() {
        return stream.head();
    }

    public Sequence<Stream, A> tail() {
        return new Stream(stream.tail()._1());
    }

    public int length() {
        return stream.length();
    }

    public <A> Functor<Stream, A> pure(A a) {
        return new Stream(fj.data.Stream.repeat(a));
    }

    public <B> Functor<Stream, B> fmap(F1<A, B> f) {
        return new Stream(stream.map(f));
    }

    public A from() {
        return stream.head();
    }

    public Iterator<A> iterator() {
        return stream.iterator();
    }

    public Empty<Stream, A> empty() {
        return new List(fj.data.Stream.nil());
    }

    public String toString(){
        if(this.length() < 1)
            return "Stream []";
        else {

            String me = "Stream [";
            for (A a : this)
                me += a + ", ";
            me = me.substring(0, me.length() - 2);
            return me + "]";
        }
    }

    public A last() {
        return stream.last();
    }

    public Sequence<Stream, A> shuffle() {
        java.util.List<A> l = new java.util.LinkedList(this.stream.toCollection());
        Collections.shuffle(l);
        return new Stream(l);
    }
}
